restana
Blazing fast, tiny and minimalist connect-like web framework for building REST micro-services.
> Check how much faster!
Uses 'find-my-way' router: https://www.npmjs.com/package/find-my-way
What else? Building ultra-fast REST APIs with Node.js (restana vs express vs fastify)
Usage
npm i restana --save
Creating the service instance
Create unsecure HTTP server:
const service = require('restana')()
Passing HTTP server instance:
const https = require('https')
const service = require('restana')({
server: https.createServer({
key: keys.serviceKey,
cert: keys.certificate
})
})
See examples:
Configuration
server
: Allows to override the HTTP server instance to be used.routerFactory
: Router factory function to allow default find-my-way
router override.prioRequestsProcessing
: If TRUE
, HTTP requests processing/handling is prioritized using setImmediate
. Default value: TRUE
ignoreTrailingSlash
: If TRUE
, trailing slashes on routes are ignored. Default value: FALSE
allowUnsafeRegex
: If TRUE
, potentially catastrophic exponential-time regular expressions are disabled. Default value: FALSE
maxParamLength
: Defines the custom length for parameters in parametric (standard, regex and multi) routes. Default value: 100
defaultRoute
: Default route handler when no route match occurs. Default value: ((req, res) => res.send(404))
disableResponseEvent
: If TRUE
, there won't be response
events triggered on the res
object. Default value: FALSE
errorHandler
: Optional global error handler function. Default value: (err, req, res) => res.send(err)
service.getConfigOptions()
service.getServer()
Example usage:
const service = require('restana')({
ignoreTrailingSlash: true
});
Optionally overwrite router factory method:
In this example we use anumargak
router instead of find-my-way
.
const anumargak = require('anumargak')
const service = require('restana')({
routerFactory: (options) => {
return anumargak(options)
}
})
...
Please consider that when using anumargak
router, request params are accessible via: req._path.params
Creating a micro-service & routes registration
const bodyParser = require('body-parser')
const service = require('restana')()
service.use(bodyParser.json())
const PetsModel = {
}
service
.get('/pets/:id', async (req, res) => {
res.send(await PetsModel.findOne(req.params.id))
})
.get('/pets', async (req, res) => {
res.send(await PetsModel.find())
})
.delete('/pets/:id', async (req, res) => {
res.send(await PetsModel.destroy(req.params.id))
})
.post('/pets/:name/:age', async (req, res) => {
res.send(await PetsModel.create(req.params))
})
.patch('/pets/:id', async (req, res) => {
res.send(await PetsModel.update(req.params.id, req.body))
})
service.get('/version', function (req, res) {
res.body = {
version: '1.0.0'
}
res.send()
})
Supported HTTP methods:
const methods = ['get', 'delete', 'put', 'patch', 'post', 'head', 'options', 'trace']
Using .all routes registration
You can also register a route handler for all
supported HTTP methods:
service.all('/allmethodsroute', function (req, res) {
res.send(200)
})
Starting the service
service.start(3000).then((server) => {})
Stopping the service
service.close().then(()=> {})
Async / Await support
service.post('/star/:username', async (req, res) => {
await starService.star(req.params.username)
const stars = await starService.count(req.params.username)
return stars
})
IMPORTANT: Returned value can't be undefined
, for such cases use res.send(...
res.send('Hello World', 200, {
'x-response-time': 100
})
Acknowledge from low-level end
operation
res.send('Hello World', 200, {}, (err) => {
if (err) {
}
})
Global error handling
const service = require('restana')({
errorHandler (err, req, res) {
console.log(`Something was wrong: ${err.message || err}`)
res.send(err)
}
})
service.get('/throw', (req, res) => {
throw new Error('Upps!')
})
Middlewares support:
const service = require('restana')({})
service.use((req, res, next) => {
const now = new Date().getTime()
res.on('response', e => {
e.res.setHeader('X-Response-Time', new Date().getTime() - now)
})
return next()
});
service.get('/v1/welcome', (req, res) => {
res.send('Hello World!')
})
service.start()
Route level middlewares
Connecting middlewares to specific routes is also supported:
service.get('/hi/:name', async (req, res) => {
return 'Hello ' + req.params.name
}, {}, [(req, res, next) => {
req.params.name = req.params.name.toUpperCase()
next()
}])
Express.js like signature also supported:
service.get('/hi/:name', m1, m2, handler [, ctx])
Third party middlewares support:
Almost all middlewares using the function (req, res, next) signature format should work, considering that no custom framework feature is used.
Examples :
Async middlewares support
Starting from v3.3.x
, you can now also use async middlewares as described below:
service.use(async (req, res, next) => {
await next()
console.log('Global middlewares execution completed!')
}))
service.use(logging())
service.use(jwt())
In the same way you can also capture uncaught exceptions inside your async middlewares:
service.use(async (req, res, next) => {
try {
await next()
} catch (err) {
console.log('upps, something just happened')
res.send(err)
}
})
service.use(logging())
service.use(jwt())
NOTE: Global and Route level middlewares execution run separately!
AWS Serverless Integration
restana
is compatible with the serverless-http library, so restana based services can also run as AWS lambdas 🚀
const serverless = require('serverless-http')
const restana = require('restana')
const service = restana()
service.get('/hello', (req, res) => {
res.send('Hello World!')
})
const handler = serverless(app);
module.exports.handler = async (event, context) => {
return await handler(event, context)
}
Serving static files
You can read more about serving static files with restana in this link:
https://thejs.blog/2019/07/12/restana-static-serving-the-frontend-with-node-js-beyond-nginx/
Third party integrations
const service = restana()
service.get('/hello', (req, res) => {
res.send('Hello World!')
})
const server = http.createServer(service.callback())
Application Performance Monitoring (APM)
As a Node.js framework implementation based on the standard http
module, restana
benefits from out of the box instrumentation on
existing APM agents such as:
Elastic APM - Route Naming
"Routes Naming" discovery is not supported out of the box, but we have created our custom integration for Elastic APM:
const apm = require('elastic-apm-node').start({
secretToken: process.env.APM_SECRET_TOKEN,
serverUrl: process.env.APM_SERVER_URL
})
const elasticApm = require('restana/libs/elastic-apm')
const { patch } = elasticApm({ apm })
const service = require('restana')()
patch(service)
service.get('/hello', (req, res) => {
res.send('Hello World!')
})
Performance comparison (framework overhead)
Which is the fastest?
You can checkout restana
performance index on the "Which is the fastest" project: https://github.com/the-benchmarker/web-frameworks#full-table-1
Using this project? Let us know 🚀
https://goo.gl/forms/qlBwrf5raqfQwteH3
Breacking changes
3.x:
- Support for
turbo-http
library was dropped.